package com.example.venue_reservation_service.service.impl;

import cn.hutool.core.bean.BeanUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.example.venue_reservation_service.domain.*;
import com.example.venue_reservation_service.dto.*;
import com.example.venue_reservation_service.mapper.*;
import com.example.venue_reservation_service.service.*;
import com.example.venue_reservation_service.utils.MinioUtil;
import com.example.venue_reservation_service.utils.QRCodeGenerator;
import com.example.venue_reservation_service.vo.*;
import com.github.yulichang.wrapper.MPJLambdaWrapper;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletResponse;
import org.apache.commons.io.FilenameUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.io.InputStreamResource;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import java.io.InputStream;
import java.time.Duration;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.temporal.ChronoUnit;
import java.util.*;
import java.util.stream.Collectors;

/**
* @author 31586
* @description 针对表【venue_information】的数据库操作Service实现
* @createDate 2024-11-12 21:43:55
*/
@Service
public class InformationServiceImpl extends ServiceImpl<InformationMapper, Information>
    implements InformationService{

    @Resource
    private PriceService priceService;

    @Resource
    private TypeService typeService;

    @Resource
    private InformationMapper informationMapper;

    @Resource
    private InformationTimeMapper informationTimeMapper;


    @Resource
    private ReservationMapper reservationMapper;

    @Resource
    private UserMapper userMapper;


    @Resource
    private MinioUtil minioUtil;

    @Resource
    private PaymentService paymentService;

    @Resource
    private SlotMapper slotMapper;

    @Resource
    private VenueSlotService venueSlotService;

    @Resource
    private AdminMapper adminMapper;

    @Value("${access.url}")
    private String imgPrefix;

    @Override
    public Result getTypes(PageDTO dto) {
        Page<Type> page = new Page<>(dto.getCurrent(), dto.getSize());
        page = typeService.page(page, null);
        List<Type> types = new ArrayList<>();
        for (Type record : page.getRecords()) {
            // 获取场馆中的场地信息
            QueryWrapper<Information> wrapper = new QueryWrapper<>();
            wrapper.select("DISTINCT type")
                    .eq("type_id", record.getId());
            List<String> locations = informationMapper.selectObjs(wrapper)
                    .stream()
                    .map(obj -> (String) obj).toList();
            record.setVenues(locations);
            if(StringUtils.isNotBlank(record.getImgUrl())){
                record.setImgUrl(imgPrefix + record.getImgUrl());
            }
            types.add(record);
        }
        return Result.ok(types).message("查询成功");
    }

    @Override
    public Result getDetails(Integer typeId) {
        MPJLambdaWrapper<Information> wrapper = new MPJLambdaWrapper<>();
        wrapper.selectAll(Information.class)
                .eq("type_id", typeId)
                .select(Price::getPrice)
                .leftJoin(Price.class, Price::getVenueId, Information::getId);
        List<Information> informationList = list(wrapper);
        informationList.forEach(item -> {
            if(StringUtils.isNotBlank(item.getImgUrl())){
                item.setImgUrl(imgPrefix + item.getImgUrl());
            }
        });
        return Result.ok(informationList).message("场地信息获取成功");
    }

    @Override
    public Result getInformation(InformationDTO dto) {
        Page<Information> page = new Page<>(dto.getCurrent(), dto.getSize());
        MPJLambdaWrapper<Information> wrapper = new MPJLambdaWrapper<>();
        wrapper.selectAll(Information.class)
                .select(Price::getPrice)
                .leftJoin(Price.class, Price::getVenueId, Information::getId)
                .orderByAsc(Information::getTypeId);

        // 指定场馆
        if(dto.getType() != 0){
            wrapper.eq(Information::getTypeId, dto.getType());
        }
        // 指定关键词
        String key = dto.getKey();
        if(key!=null && !"".equals(key)){
            wrapper.like(Information::getVenueName, key);
        }

        page = page(page, wrapper);
        QueryVO<Information> vo = new QueryVO();
        vo.setTotal(page.getTotal());
        List<Information> records = page.getRecords();
        records.forEach(item -> {
            if(StringUtils.isNotBlank(item.getImgUrl())){
                item.setImgUrl(imgPrefix + item.getImgUrl());
            }
            item.setQrCode(imgPrefix + item.getQrCode());
        });
        vo.setList(records);
        return Result.ok(vo).message("场地信息查询成功");
    }

    @Override
    public Result addInformation(Information information) {
        String venueName = information.getVenueName();
        Information one = getOne(Wrappers.<Information>lambdaQuery()
                .eq(Information::getVenueName, venueName)
                .eq(Information::getTypeId, information.getTypeId()));
        if (Optional.ofNullable(one).isPresent()) {
            return Result.fail().message("当前已经存在该名称的场地");
        }
        information.setImgUrl("");
        information.setUpdateTime(LocalDateTime.now());
        save(information);

        // 生成场地二维码图片
        MultipartFile file = QRCodeGenerator.generateQRCode(information.getId() + "");
        // 保存二维码图片
        try {
            minioUtil.uploadFile(file ,file.getOriginalFilename() ,file.getContentType());

            information.setQrCode(file.getOriginalFilename());
            updateById(information);
        }catch (Exception e){
            e.printStackTrace();
            return Result.fail().message("二维码图片上传失败");
        }
        // 生成场地的消费价格
        Price price  = new Price();
        price.setPrice(0.0);
        price.setVenueId(information.getId());
        priceService.save(price);
        return Result.ok().message("场地信息添加成功");
    }

    @Override
    public Result updateInformation(Information information) {
        Information byId = getById(information.getId());
        String venueName = information.getVenueName();
        if(!venueName.equals(byId.getVenueName())){
            // 修改了场地的名称
            Information one = getOne(Wrappers.<Information>lambdaQuery().eq(Information::getVenueName, venueName));
            if (Optional.ofNullable(one).isPresent()) {
                return Result.fail().message("当前已经存在该名称的场地");
            }
        }
        updateById(information);
        return Result.ok().message("场地信息修改成功");
    }

    @Override
    public Result delInformation(Integer id) {
        Information information = getById(id);
        if (Optional.ofNullable(information).isEmpty()) {
            return Result.fail().message("场地信息不存在");
        }
        // 判断当前场地是否有待签到的订单信息
        int size = reservationMapper.selectList(Wrappers.<Reservation>lambdaQuery()
                .eq(Reservation::getVenueId, id)
                .eq(Reservation::getAuditStatus, 1)
                .or()
                .eq(Reservation::getVenueId, id)
                .eq(Reservation::getAuditStatus, 2)
        ).size();
        if (size>0) {
            return Result.fail().message("场地存在待完成预约数据");
        }

        // 删除预约记录
        for (Reservation reservation : reservationMapper.selectList(Wrappers.<Reservation>lambdaQuery().eq(Reservation::getVenueId, id))) {
            reservationMapper.deleteById(reservation.getId());
            // 删除关联的支付订单数据
            paymentService.removeById(reservation.getPayId());
        }
        // 删除时间段关联数据
        informationTimeMapper.delete(Wrappers.<InformationTime>lambdaQuery().eq(InformationTime::getVenueId, id));
        // 删除场地的价格数据
        priceService.remove(Wrappers.<Price>lambdaQuery().eq(Price::getVenueId, id));

        // 删除场地的照片信息
        if (StringUtils.isNotBlank(information.getImgUrl())) {
            minioUtil.removeFile(information.getImgUrl().split("/")[information.getImgUrl().split("/").length-1]);
        }
        // 删除场地的二维码照片
        if (StringUtils.isNotBlank(information.getQrCode())) {
            minioUtil.removeFile(information.getQrCode().split("/")[information.getQrCode().split("/").length-1]);
        }

        // 移除场地信息
        removeById(id);
        return Result.ok().message("场地信息移除成功");
    }

    @Override
    public Result updatePrice(Price price) {
        priceService.updatePrice(price);
        return Result.ok().message("场地费用修改成功");
    }

    @Override
    public Result classifyInformation(TypeDTO dto) {
        Page<Information> page = new Page<>(dto.getCurrent(), dto.getSize());
        page = page(page, Wrappers.<Information>lambdaQuery()
                .eq(Information::getType, dto.getType())
                .eq(Information::getTypeId, dto.getTypeId()));
        QueryVO<Information> vo = new QueryVO<>();
        vo.setTotal(page.getTotal());
        vo.setList(page.getRecords());
        return Result.ok(vo).message("分类信息获取成功");
    }

    @Override
    public Result getConditions(QueryDTO dto) {
        LocalDate today = dto.getReservationDate();

        // 判定时间
        if (today.isBefore(LocalDate.now())) {
            return Result.ok().message("请选择正确日期");
        }

        // 判断场馆及场地数据是否存在
        Type type = typeService.getById(dto.getTypeId());
        if (Optional.ofNullable(type).isEmpty()) {
            return Result.fail().message("未查询到场馆数据");
        }
        Information information = getById(dto.getInformationId());
        if (Optional.ofNullable(information).isEmpty()) {
            return Result.fail().message("未查询到场地数据");
        }

        // 加载在场馆营业时间下场地的可以预约时间
        LocalTime openTime = type.getOpenTime();
        LocalTime closeTime = type.getCloseTime();

        if (LocalTime.now().isAfter(closeTime) && today.compareTo(LocalDate.now()) ==0){
            // 如果当前时间已经到了今天的闭馆时间
            return Result.ok().message("已到闭馆时间");
        }


        // 排除预约数据（待签到、已签到）
        List<Reservation> reservations = reservationMapper.selectList(Wrappers.<Reservation>lambdaQuery()
                .eq(Reservation::getVenueId, dto.getInformationId())
                .eq(Reservation::getReservationDate, today)
                .in(Reservation::getAuditStatus, 1, 2));

        // 找到今天有哪些禁用时间段
        List<Slot> slots = slotMapper.selectList(Wrappers.<Slot>lambdaQuery()
                .eq(Slot::getReservationDate, today));


        // 查询当前日期下场地的被预约情况
        // 截断到整点
        LocalTime truncatedStart = (today.compareTo(LocalDate.now()) == 0 && openTime.isBefore(LocalTime.now())) ? LocalTime.now().truncatedTo(ChronoUnit.HOURS) : openTime.truncatedTo(ChronoUnit.HOURS);
        LocalTime truncatedEnd = closeTime.truncatedTo(ChronoUnit.HOURS);

        // 计算差值
        Duration duration = Duration.between(truncatedStart,  truncatedEnd);
        int hours = Integer.parseInt(duration.toHours() + "");

        List<TimeItem> times = new ArrayList<>();
        boolean isAdd = true;
        for (int i=0;i< hours;i++){
            int startHour = truncatedStart.getHour() + i;
            for (Reservation reservation : reservations) {
                int start = reservation.getStartTime().getHour();
                int end = reservation.getEndTime().getHour();
                if(startHour >= start && startHour < end) {
                    isAdd = false;
                    break;
                }
            }

            if (isAdd){
                for (Slot slot : slots) {
                    int start = slot.getSlotStart().getHour();
                    int end = slot.getSlotEnd().getHour();
                    if(startHour >= start && startHour < end) {
                        // 检查是否存在相关的场地
                        List<VenueSlot> list = venueSlotService.list(Wrappers.<VenueSlot>lambdaQuery()
                                .eq(VenueSlot::getSlotId, slot.getId())
                                .eq(VenueSlot::getVenueId, information.getId()));
                        if(list.size() > 0) {
                            isAdd = false;
                            break;
                        }
                    }
                }
            }

            if(isAdd){
                TimeItem time = new TimeItem();
                time.setId(times.size() + 1);
                time.setStartTime(LocalTime.of(truncatedStart.getHour() + i, 0));
                time.setEndTime(LocalTime.of(truncatedStart.getHour() + i + 1, 0));
                times.add(time);
            }
            isAdd = true;
        }

        InformationVO vo = BeanUtil.copyProperties(information, InformationVO.class);
        vo.setTimes(times);
        // 获取场地使用价格
        Price price = priceService.getOne(Wrappers.<Price>lambdaQuery().eq(Price::getVenueId, information.getId()));
        if (Optional.ofNullable(price).isPresent()){
            vo.setPrice(price.getPrice());
        }else {
            vo.setPrice(0.0);
        }
        return Result.ok(vo).message("查询成功");
    }

    @Override
    public Result viewInformation(ViewDTO dto) {
        Integer adminId = dto.getAdminId();
        User user = userMapper.selectById(adminId);
        if(user == null || user.getType() == 1 || user.getType() == 2){
            return Result.fail().message("未查询到管理员信息");
        }
        // 获取管理的场馆
        Admin admin = adminMapper.selectOne(Wrappers.<Admin>lambdaQuery().eq(Admin::getUserId, user.getId()));
        if (Optional.ofNullable(admin).isEmpty()) {
            return Result.fail().message("未查询到当前管理员账号管理的场馆信息");
        }
        Type type = typeService.getById(admin.getTypeId());
        if (Optional.ofNullable(type).isEmpty()) {
            return Result.fail().message("未查询到场馆信息");
        }
        // 获取场馆里面场地的编号
        List<Integer> list = informationMapper.selectObjs(Wrappers.<Information>lambdaQuery()
                        .select(Information::getId)
                        .eq(Information::getTypeId, type.getId()))
                .stream()
                .map(obj -> (Integer) obj)
                .toList();
        // 查询相关订单
        LambdaQueryWrapper<Reservation> wrapper = Wrappers.<Reservation>lambdaQuery();
        wrapper.eq(Reservation::getReservationDate, dto.getDate())
                .in(Reservation::getVenueId, list)
                .eq(Reservation::getAuditStatus, 1)
                .orderByAsc(Reservation::getStartTime);
        // 检查用户信息
        String phone = dto.getPhone();
        if(phone != null && !"".equals(phone)){
            User one = userMapper.selectOne(Wrappers.<User>lambdaQuery()
                    .like(User::getUserPhone, phone));
                wrapper.eq(Reservation::getUserId, one != null ?one.getId():-1);
        }

        List<Reservation> reservations = reservationMapper.selectPage(new Page<Reservation>(dto.getCurrent(), dto.getSize()) , wrapper).getRecords();
        reservations.forEach(item -> {
            String s = userMapper.selectById(item.getUserId()).getUserPhone();
            item.setPhone(s);
        });
        return Result.ok(reservations).message("查询成功");
    }

    @Override
    public Result adminMessage(SQueryDTO dto) {
        User user = userMapper.selectById(dto.getUserId());
        // 用户信息判空
        if (Optional.ofNullable(user).isEmpty()) {
            return Result.fail().message("用户信息不存在");
        }
        if (user.getType() !=3) {
            return Result.fail().message("当前账号非管理员");
        }

        // 加载场地数据
        Information information = informationMapper.selectById(dto.getVenueId());
        if (Optional.ofNullable(information).isEmpty()) {
            return Result.fail().message("场地信息不存在");
        }
        // 加载场馆数据
        Type type = typeService.getById(information.getTypeId());
        if (Optional.ofNullable(type).isEmpty()) {
            return Result.fail().message("场馆数据不存在");
        }

        LocalTime openTime = type.getOpenTime();
        LocalTime closeTime = type.getCloseTime();

        // 加载场地的使用情况
        // 查询当前日期下场地的被预约情况
        // 截断到整点
        LocalTime truncatedStart = openTime.truncatedTo(ChronoUnit.HOURS);
        LocalTime truncatedEnd = closeTime.truncatedTo(ChronoUnit.HOURS);

        // 计算差值
        Duration duration = Duration.between(truncatedStart,  truncatedEnd);
        int hours = Integer.parseInt(duration.toHours() + "");

        AdminVO adminVO = new AdminVO();
        adminVO.setVenueId(dto.getVenueId());
        adminVO.setVenueName(information.getVenueName());
        adminVO.setToday(dto.getReservationDate());

        List<AdminTime> adminTimeList = new ArrayList<>();
        for (int i=0 ; i<hours; i++){
            LocalTime start = openTime.plusHours(i);
            LocalTime end = openTime.plusHours(i + 1);
            AdminTime adminTime = new AdminTime();
            adminTime.setId(i);
            adminTime.setStart(start);
            adminTime.setEnd(end);
            adminTime.setUseStatus(0);

            // 在预约记录表中查询是否存在记录
            int rSize = reservationMapper.selectList(Wrappers.<Reservation>
                                    lambdaQuery()
                            .eq(Reservation::getVenueId, dto.getVenueId())
                            .in(Reservation::getAuditStatus, 1, 2)
                            .eq(Reservation::getReservationDate, dto.getReservationDate())
                            .and(wrap -> {  // 嵌套 OR 条件：( (slot_start < ? AND slot_end > ? ) OR ... )
                                wrap.or(sub -> sub
                                                .lt(Reservation::getStartTime, start)
                                                .gt(Reservation::getEndTime, start)
                                        )
                                        .or(sub -> sub
                                                .lt(Reservation::getStartTime, end)
                                                .gt(Reservation::getEndTime, end)
                                        )
                                        .or(sub -> sub
                                                .le(Reservation::getStartTime, start)
                                                .ge(Reservation::getEndTime, end)
                                        );
                            }))
                    .size();
            if (rSize > 0){
                adminTime.setUseStatus(1);
                break;
            }

            // 查询禁用表格状态
            List<Slot> slots = slotMapper.selectList(Wrappers.<Slot>lambdaQuery()
                    .eq(Slot::getReservationDate, dto.getReservationDate())
                    .and(wrap -> {  // 嵌套 OR 条件：( (slot_start < ? AND slot_end > ? ) OR ... )
                        wrap.or(sub -> sub
                                        .lt(Slot::getSlotStart, start)
                                        .gt(Slot::getSlotEnd, start)
                                )
                                .or(sub -> sub
                                        .lt(Slot::getSlotStart, end)
                                        .gt(Slot::getSlotEnd, end)
                                )
                                .or(sub -> sub
                                        .le(Slot::getSlotStart, start)
                                        .ge(Slot::getSlotEnd, end)
                                );
                    }));

            for (Slot slot : slots) {
                List<VenueSlot> list = venueSlotService.list(Wrappers.<VenueSlot>lambdaQuery()
                        .eq(VenueSlot::getSlotId, slot.getId())
                        .eq(VenueSlot::getVenueId, information.getId()));
                if(list.size() > 0){
                    adminTime.setUseStatus(2);
                }
            }
            adminTimeList.add(adminTime);
        }

        adminVO.setTimes(adminTimeList);

        return Result.ok(adminVO).message("场地数据加载成功");
    }

    @Override
    public ResponseEntity<InputStreamResource> QRDownload(Integer id, HttpServletResponse response) {
        Information information = getById(id);
        if (Optional.ofNullable(information).isEmpty()) {
            return null;
        }
        String[] split = information.getQrCode().split("/");
        ResponseEntity<InputStreamResource> result = null;
        try{
            InputStream object = minioUtil.getObject(split[split.length - 1]);
            try {
                InputStreamResource resource = new InputStreamResource(object);

                // 设置响应头
                HttpHeaders headers = new HttpHeaders();
                // 设置 Content-Disposition 头，指定文件名，attachment 表示以附件形式下载
                headers.add(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename="+ split[split.length - 1]);
                // 设置 Content-Type 头
                MediaType mediaType = MediaType.IMAGE_PNG;

                // 返回响应实体，将资源和响应头信息封装到 ResponseEntity 中
                result = ResponseEntity.ok()
                        .headers(headers)
                        .contentType(mediaType)
                        .body(resource);
                object.close();
            } catch (Exception e) {
                // 处理异常
                e.printStackTrace();
                return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR);
            }
        }catch (Exception e){
            e.printStackTrace();
            return null;
        }
        return result;
    }

    @Override
    public List<Integer> getIdsByTypeId(Integer typeId) {
        // 查看在当前场馆编号下，所有的场地信息
        QueryWrapper<Information> wrapper = new QueryWrapper<>();
        wrapper.select("DISTINCT id")
                .eq("type_id", typeId);

        List<Integer> venueIds = informationMapper.selectObjs(wrapper)
                .stream()
                .map(obj -> (Integer) obj).toList();

        return venueIds;
    }

    @Override
    public Result upload(Integer id, MultipartFile file) {
        Information information = getById(id);
        if (Optional.ofNullable(information).isEmpty()) {
            return Result.fail().message("场地信息不存在");
        }

        try {
            // 删除原来的照片
            String imgUrl = information.getImgUrl();
            if (imgUrl!=null && !"".equals(imgUrl)){
                String[] split = imgUrl.split("/");
                // 获取照片名字
                String img = split[split.length - 1];
                minioUtil.removeFile(img);
            }

            // 获取文件后缀名
            String extension = FilenameUtils.getExtension( file.getOriginalFilename());
            // 重新生成文件名称
            String filename = UUID.randomUUID() + "-" + id + "." + extension;
            // 调用minio接口上传照片
            minioUtil.uploadFile(file, filename, file.getContentType());

            information.setImgUrl(filename);
            updateById(information);

        } catch (Exception e) {
            e.printStackTrace();
            return Result.fail().message("图片上传失败");
        }
        return Result.ok().message("图片上传成功");
    }

    @Override
    public Result queryTypes() {
        QueryWrapper<Information> wrapper = new QueryWrapper<>();
        wrapper.select("DISTINCT type");
        return Result.ok(list(wrapper).stream().map(Information::getType).toList()).message("ok");
    }

}




